
/*
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * graphml
 *     desc
 *     key
 *         desc
 *     data
 *     graph
 *         desc
 *         data
 *         node
 *             desc
 *             data
 *             port
 *             graph
 *         edge
 *             desc
 *             data
 *             graph
 *         hyperedge
 *             desc
 *             data
 *             endpoint
 *             desc
 *             graph
 *         locator
 */

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*
 * COMPAT using xml-config --cflags to get the include path this will
 * work with both 
 */
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>

#include "main.h"
#include "options.h"
#include "uniqstring.h"
#include "uniqnode.h"
#include "nes.h"
#include "splay-tree.h"
#include "misc.h"
#include "graphml.h"

/* type of data in key entry */
#define GRAPHML_T_UNKNOWN 0
#define GRAPHML_T_BOOLEAN 1
#define GRAPHML_T_INT 2
#define GRAPHML_T_LONG 3
#define GRAPHML_T_FLOAT 4
#define GRAPHML_T_DOUBLE 5
#define GRAPHML_T_STRING 6

/* the types in 'for' at a key */
#define GRAPHML_F_UNKNOWN 0
#define GRAPHML_F_GRAPHML 1
#define GRAPHML_F_GRAPH 2
#define GRAPHML_F_NODE 3
#define GRAPHML_F_EDGE 4
#define GRAPHML_F_HYPEREDGE 5
#define GRAPHML_F_PORT 6
#define GRAPHML_F_ENDPOINT 7
#define GRAPHML_F_ALL 8

/* data retreived from key statement */
struct keyinfo
{
  char *name;			/* name of attribute */
  int type;			/* type of data, int, long, string etc */
  char *defaults;		/* NULL or optional default setting */
};

/* splay storage for the keys data */
static splay_tree keysgraphml = NULL;
static splay_tree keysgraph = NULL;
static splay_tree keysnode = NULL;
static splay_tree keysedge = NULL;
static splay_tree keyshyperedge = NULL;
static splay_tree keysport = NULL;
static splay_tree keysendpoint = NULL;

static xmlDocPtr rootdoc = NULL;

static void graphml_splay_tree_delete_value_fn (splay_tree_value value);
static void parsegraphmlkey (xmlNodePtr cur);
static void parsegraphml2 (xmlNodePtr cur, struct usubg *rootedon);
static void graphmlgraph (xmlNodePtr cur, struct usubg *rootedon);
static void graphmlnode (xmlNodePtr cur, struct usubg *rootedon);
static void graphmledge (xmlNodePtr cur, struct usubg *rootedon);
static void graphmlhyperedge (xmlNodePtr cur, struct usubg *rootedon);
static void graphmllocator (xmlNodePtr cur, struct usubg *rootedon);
static void graphmlgraphdata (xmlNodePtr cur, struct usubg *rootedon);
static int parsebisonxml (char *fname, FILE * fstream, xmlNodePtr cur);


int
graphml_lineno (void)
{
  return (0);
}

int
parsegraphml (char *fname, FILE * fstream)
{
  int ret = 0;
  xmlNodePtr cur;
  xmlNodePtr cur_node;
  xmlNsPtr ns;

  if (option_debug)
    {
      printf ("%s(): starting with file '%s'\n", __FUNCTION__, fname);
    }

  /* */
  xmlKeepBlanksDefault (0);

  rootdoc = xmlParseFile (fname);

  if (rootdoc == NULL)
    {
      return (0);
    }

  /* first token in document */
  cur = xmlDocGetRootElement (rootdoc);

  if (cur == NULL)
    {
      xmlFreeDoc (rootdoc);
      /* Clean up everything else before quitting. */
      xmlCleanupParser ();
      return (0);
    }

  /* <graphml xmlns="http://graphml.graphdrawing.org/xmlns" */
  ns = xmlSearchNsByHref (rootdoc, cur,
			  (const xmlChar *)
			  "http://graphml.graphdrawing.org/xmlns");

  if (ns)
    {
    }

  if (xmlStrcmp (cur->name, (const xmlChar *) "graphml"))
    {
      /* try GNU bison xml */
      ret = parsebisonxml (fname, fstream, cur);
      /* ready with document */
      xmlFreeDoc (rootdoc);
      /* Clean up everything else before quitting. */
      xmlCleanupParser ();
      return (ret);
    }


  if (option_debug)
    {
      printf ("%s(): graphml %s\n", __FUNCTION__, cur->name);
    }

  /* splay storage for the keys data */
  keysgraphml =
    splay_tree_new (splay_tree_compare_strings, NULL,
		    graphml_splay_tree_delete_value_fn);
  keysgraph =
    splay_tree_new (splay_tree_compare_strings, NULL,
		    graphml_splay_tree_delete_value_fn);
  keysnode =
    splay_tree_new (splay_tree_compare_strings, NULL,
		    graphml_splay_tree_delete_value_fn);
  keysedge =
    splay_tree_new (splay_tree_compare_strings, NULL,
		    graphml_splay_tree_delete_value_fn);
  keyshyperedge =
    splay_tree_new (splay_tree_compare_strings, NULL,
		    graphml_splay_tree_delete_value_fn);
  keysport =
    splay_tree_new (splay_tree_compare_strings, NULL,
		    graphml_splay_tree_delete_value_fn);
  keysendpoint =
    splay_tree_new (splay_tree_compare_strings, NULL,
		    graphml_splay_tree_delete_value_fn);

  /* scan data in the xml file */
  for (cur_node = cur->children; cur_node != NULL; cur_node = cur_node->next)
    {

      if (option_debug)
	{
	  printf ("%s(): graphml-ch %s\n", __FUNCTION__, cur_node->name);
	}

      if (xmlStrcmp (cur_node->name, (const xmlChar *) "graph") == 0)
	{
	  /* start at the root graph */
	  parsegraphml2 (cur_node, (struct usubg *) 0);
	}
      else if (xmlStrcmp (cur_node->name, (const xmlChar *) "key") == 0)
	{
	  parsegraphmlkey (cur_node);
	}
      else
	{
	  /* desc */
	}

    }

  /* splay storage for the keys data */
  splay_tree_delete (keysgraphml);
  splay_tree_delete (keysgraph);
  splay_tree_delete (keysnode);
  splay_tree_delete (keysedge);
  splay_tree_delete (keyshyperedge);
  splay_tree_delete (keysport);
  splay_tree_delete (keysendpoint);

  /* Clean up everything else before quitting. */
  xmlCleanupParser ();

  return (0);
}

/* The type of a function used to deallocate any resources associated
   with the value.  */
static void
graphml_splay_tree_delete_value_fn (splay_tree_value value)
{
  if (value)
    {
      mgr_free ((void *) value);
    }
  return;
}

/* key for the attributes in the graph */
static void
parsegraphmlkey (xmlNodePtr cur)
{
  xmlNodePtr cur_node;
  splay_tree usetree = NULL;
  const xmlChar *keyid = NULL;
  const xmlChar *keyfor = NULL;
  const xmlChar *keyattrname = NULL;
  const xmlChar *keyattrtype = NULL;
  const xmlChar *keydefault = NULL;
  int typetype = GRAPHML_T_UNKNOWN;
  int fortype = GRAPHML_F_UNKNOWN;
  char *keyidstring = NULL;
  char *keyattrnamestring = NULL;
  char *keydefaultstring = NULL;
  splay_tree_node spn = NULL;
  struct keyinfo *keydata = NULL;

  keyid = xmlGetProp (cur, (const xmlChar *) "id");
  keyidstring = uniqstring ((char *) keyid);

  keyfor = xmlGetProp (cur, (const xmlChar *) "for");

  keyattrname = xmlGetProp (cur, (const xmlChar *) "attr.name");
  keyattrnamestring = uniqstring ((char *) keyattrname);

  keyattrtype = xmlGetProp (cur, (const xmlChar *) "attr.type");

  if (option_debug)
    {
      printf ("%s(): key %s for %s name %s type %s\n", __FUNCTION__, keyid,
	      keyfor, keyattrname, keyattrtype);
    }

  /* dtd: for (graphml|graph|node|edge|hyperedge|port|endpoint|all)
   *
   * <!ELEMENT key (#PCDATA)>
   * <!ATTLIST key 
   *          id  ID                                            #REQUIRED
   *          for (graphml|graph|node|edge|hyperedge|port|endpoint|all) "all"
   * >
   */
  if (xmlStrcmp (keyfor, (const xmlChar *) "graphml") == 0)
    {
      fortype = GRAPHML_F_GRAPHML;
    }
  else if (xmlStrcmp (keyfor, (const xmlChar *) "graph") == 0)
    {
      fortype = GRAPHML_F_GRAPH;
    }
  else if (xmlStrcmp (keyfor, (const xmlChar *) "node") == 0)
    {
      fortype = GRAPHML_F_NODE;
    }
  else if (xmlStrcmp (keyfor, (const xmlChar *) "edge") == 0)
    {
      fortype = GRAPHML_F_EDGE;
    }
  else if (xmlStrcmp (keyfor, (const xmlChar *) "hyperedge") == 0)
    {
      fortype = GRAPHML_F_HYPEREDGE;
    }
  else if (xmlStrcmp (keyfor, (const xmlChar *) "endpoint") == 0)
    {
      fortype = GRAPHML_F_PORT;
    }
  else if (xmlStrcmp (keyfor, (const xmlChar *) "all") == 0)
    {
      fortype = GRAPHML_F_ALL;
    }
  else
    {
      fortype = GRAPHML_F_UNKNOWN;
    }

  /* the types in 'for' at a key */
  switch (fortype)
    {
    case GRAPHML_F_UNKNOWN:
      usetree = NULL;
      break;
    case GRAPHML_F_GRAPHML:
      usetree = keysgraphml;
      break;
    case GRAPHML_F_GRAPH:
      usetree = keysgraph;
      break;
    case GRAPHML_F_NODE:
      usetree = keysnode;
      break;
    case GRAPHML_F_EDGE:
      usetree = keysedge;
      break;
    case GRAPHML_F_HYPEREDGE:
      usetree = keyshyperedge;
      break;
    case GRAPHML_F_PORT:
      usetree = keysport;
      break;
    case GRAPHML_F_ENDPOINT:
      usetree = keysendpoint;
      break;
    case GRAPHML_F_ALL:
      usetree = NULL;
      break;
    default:
      usetree = NULL;
      break;
    }

  /*
   * <xs:simpleType name="key.type.type" final="#all">
   *   <xs:annotation>
   *     <xs:documentation 
   *         source="http://graphml.graphdrawing.org/(Dokumentation der Attributes Erweiterung; entsprechende Stelle.html)"
   *         xml:lang="en">
   *       Simple type for the attr.type attribute of &lt;key>.
   *       key.type.type is final, that is, it may not be extended
   *                           or restricted.
   *       key.type.type is a restriction of xs:NMTOKEN
   *       Allowed values: boolean, int, long, float, double, string.
   *     </xs:documentation>
   *   </xs:annotation>
   *   <xs:restriction base="xs:NMTOKEN">  
   *     <xs:enumeration value="boolean"/>
   *     <xs:enumeration value="int"/>
   *     <xs:enumeration value="long"/>
   *     <xs:enumeration value="float"/>
   *     <xs:enumeration value="double"/>
   *     <xs:enumeration value="string"/>
   *   </xs:restriction>
   * </xs:simpleType>
   */
  if (xmlStrcmp (keyattrtype, (const xmlChar *) "boolean") == 0)
    {
      typetype = GRAPHML_T_BOOLEAN;
    }
  else if (xmlStrcmp (keyattrtype, (const xmlChar *) "int") == 0)
    {
      typetype = GRAPHML_T_INT;
    }
  else if (xmlStrcmp (keyattrtype, (const xmlChar *) "long") == 0)
    {
      typetype = GRAPHML_T_LONG;
    }
  else if (xmlStrcmp (keyattrtype, (const xmlChar *) "float") == 0)
    {
      typetype = GRAPHML_T_FLOAT;
    }
  else if (xmlStrcmp (keyattrtype, (const xmlChar *) "double") == 0)
    {
      typetype = GRAPHML_T_DOUBLE;
    }
  else if (xmlStrcmp (keyattrtype, (const xmlChar *) "string") == 0)
    {
      typetype = GRAPHML_T_STRING;
    }
  else
    {
      typetype = GRAPHML_T_UNKNOWN;
    }

  /* optional default setting part */
  for (cur_node = cur->children; cur_node != NULL; cur_node = cur_node->next)
    {
      if (option_debug)
	{
	  printf ("%s(): key-ch %s\n", __FUNCTION__, cur_node->name);
	}

      if (xmlStrcmp (cur_node->name, (const xmlChar *) "default") == 0)
	{
	  keydefault = xmlNodeGetContent (cur_node);
	  keydefaultstring = uniqstring ((char *) keydefault);
	}
    }

  if (option_debug)
    {
      printf ("%s(): default is %s\n", __FUNCTION__, keydefaultstring);
    }

  /* add to all keys or one key */
  if (fortype == GRAPHML_F_ALL)
    {
      usetree = keysgraphml;
      spn = splay_tree_lookup (usetree, (splay_tree_key) keyidstring);
      if (spn == NULL)
	{
	  keydata = mgr_malloc (sizeof (struct keyinfo));
	  keydata->name = keyattrnamestring;
	  keydata->type = typetype;
	  keydata->defaults = keydefaultstring;
	  spn = splay_tree_insert (usetree, (splay_tree_key) keyidstring, (splay_tree_value) keydata	/* value */
	    );
	}
      usetree = keysgraph;
      spn = splay_tree_lookup (usetree, (splay_tree_key) keyidstring);
      if (spn == NULL)
	{
	  keydata = mgr_malloc (sizeof (struct keyinfo));
	  keydata->name = keyattrnamestring;
	  keydata->type = typetype;
	  keydata->defaults = keydefaultstring;
	  spn = splay_tree_insert (usetree, (splay_tree_key) keyidstring, (splay_tree_value) keydata	/* value */
	    );
	}
      usetree = keysnode;
      spn = splay_tree_lookup (usetree, (splay_tree_key) keyidstring);
      if (spn == NULL)
	{
	  keydata = mgr_malloc (sizeof (struct keyinfo));
	  keydata->name = keyattrnamestring;
	  keydata->type = typetype;
	  keydata->defaults = keydefaultstring;
	  spn = splay_tree_insert (usetree, (splay_tree_key) keyidstring, (splay_tree_value) keydata	/* value */
	    );
	}
      usetree = keysedge;
      spn = splay_tree_lookup (usetree, (splay_tree_key) keyidstring);
      if (spn == NULL)
	{
	  keydata = mgr_malloc (sizeof (struct keyinfo));
	  keydata->name = keyattrnamestring;
	  keydata->type = typetype;
	  keydata->defaults = keydefaultstring;
	  spn = splay_tree_insert (usetree, (splay_tree_key) keyidstring, (splay_tree_value) keydata	/* value */
	    );
	}
      usetree = keyshyperedge;
      spn = splay_tree_lookup (usetree, (splay_tree_key) keyidstring);
      if (spn == NULL)
	{
	  keydata = mgr_malloc (sizeof (struct keyinfo));
	  keydata->name = keyattrnamestring;
	  keydata->type = typetype;
	  keydata->defaults = keydefaultstring;
	  spn = splay_tree_insert (usetree, (splay_tree_key) keyidstring, (splay_tree_value) keydata	/* value */
	    );
	}
      usetree = keysport;
      spn = splay_tree_lookup (usetree, (splay_tree_key) keyidstring);
      if (spn == NULL)
	{
	  keydata = mgr_malloc (sizeof (struct keyinfo));
	  keydata->name = keyattrnamestring;
	  keydata->type = typetype;
	  keydata->defaults = keydefaultstring;
	  spn = splay_tree_insert (usetree, (splay_tree_key) keyidstring, (splay_tree_value) keydata	/* value */
	    );
	}
      usetree = keysendpoint;
      spn = splay_tree_lookup (usetree, (splay_tree_key) keyidstring);
      if (spn == NULL)
	{
	  keydata = mgr_malloc (sizeof (struct keyinfo));
	  keydata->name = keyattrnamestring;
	  keydata->type = typetype;
	  keydata->defaults = keydefaultstring;
	  spn = splay_tree_insert (usetree, (splay_tree_key) keyidstring, (splay_tree_value) keydata	/* value */
	    );
	}
    }
  else
    {
      if (usetree)
	{
	  spn = splay_tree_lookup (usetree, (splay_tree_key) keyidstring);
	  if (spn == NULL)
	    {
	      keydata = mgr_malloc (sizeof (struct keyinfo));
	      keydata->name = keyattrnamestring;
	      keydata->type = typetype;
	      keydata->defaults = keydefaultstring;
	      spn = splay_tree_insert (usetree, (splay_tree_key) keyidstring, (splay_tree_value) keydata	/* value */
		);
	    }
	}
    }

  return;
}

static void
parsegraphml2 (xmlNodePtr cur, struct usubg *rootedon)
{
  xmlNodePtr cur_node;

  for (cur_node = cur->children; cur_node != NULL; cur_node = cur_node->next)
    {
      if (option_debug)
	{
	  printf ("%s(): graph2 %s\n", __FUNCTION__, cur_node->name);
	}

      if (xmlStrcmp (cur_node->name, (const xmlChar *) "node") == 0)
	{
	  graphmlnode (cur_node, rootedon);
	}
      else if (xmlStrcmp (cur_node->name, (const xmlChar *) "edge") == 0)
	{
	  graphmledge (cur_node, rootedon);
	}
      else if (xmlStrcmp (cur_node->name, (const xmlChar *) "hyperedge") == 0)
	{
	  graphmlhyperedge (cur_node, rootedon);
	}
      else if (xmlStrcmp (cur_node->name, (const xmlChar *) "locator") == 0)
	{
	  graphmllocator (cur_node, rootedon);
	}
      else if (xmlStrcmp (cur_node->name, (const xmlChar *) "graph") == 0)
	{
	  graphmlgraph (cur_node, rootedon);
	}
      else if (xmlStrcmp (cur_node->name, (const xmlChar *) "data") == 0)
	{
	  graphmlgraphdata (cur_node, rootedon);
	}
      else
	{
	  /* desc */
	}
    }

  return;
}

/* new subgraph started */
static void
graphmlgraph (xmlNodePtr cur, struct usubg *subg)
{
  struct usubg *newsg = NULL;
  char *sgname = NULL;
  const xmlChar *graphid = NULL;

  graphid = xmlGetProp (cur, (const xmlChar *) "id");

  sgname = uniqstring ((char *) graphid);

  newsg = usubg_new (sgname, subg);

  if (option_init_unfolded)
    {
      newsg->bitflags.folded = 0;
    }

  if (option_init_folded)
    {
      newsg->bitflags.folded = 1;
    }

  usubg_add (newsg, subg);
  newsg->summaryn->rootedon = subg;
  newsg->summaryn->color = 0x00ffffff;
  /* this is a subgraph summary node */
  newsg->summaryn->bitflags.sumnode = 1;

  parsegraphml2 (cur, newsg);

  if (newsg->sg == NULL && newsg->nl == NULL)
    {
      /* empty subgraph is folded */
      newsg->bitflags.folded = 1;
    }

  return;
}

static void
graphmlnode (xmlNodePtr cur, struct usubg *subg)
{
  xmlNodePtr cur_node;
  const xmlChar *nodeid = NULL;
  struct unode *un = NULL;
  char *laststring = NULL;

  cur_node = cur;

  nodeid = xmlGetProp (cur_node, (const xmlChar *) "id");

  if (option_debug)
    {
      printf ("%s(): nodeid=%s\n", __FUNCTION__, (char *) nodeid);
    }

  laststring = uniqstring ((char *) nodeid);

  un = uniqnode (laststring);

  if (un == (struct unode *) 0)
    {
      un = unode_new (laststring);
      un->label = un->name;
      un->rootedon = subg;
      /* node is defined by node statement */
      un->bitflags.defbynode = 1;
      un->fontsize = 0;
      uniqnode_add (un->name, un);
      unode_append_nodelist (un);
      usubg_add_node (un, subg);
    }
  else
    {
      if (un->rootedon == subg)
	{
	  /* oke */
	}
      else
	{
	  /* node is created in edge() but defined in node() in this other subgraph */
	  un->rootedon = subg;
	  usubg_add_node (un, subg);
	}
    }

  for (cur_node = cur->children; cur_node != NULL; cur_node = cur_node->next)
    {
      if (option_debug)
	{
	  printf ("%s(): node-ch %s <-\n", __FUNCTION__, cur_node->name);
	}
    }

  return;
}

static void
graphmledge (xmlNodePtr cur, struct usubg *subg)
{
  xmlNodePtr cur_node;
  const xmlChar *edgeid = NULL;
  const xmlChar *edgesource = NULL;
  const xmlChar *edgetarget = NULL;
  char *fromname = NULL;
  char *toname = NULL;
  struct unode *fn = NULL;
  struct unode *tn = NULL;
  struct uedge *ue = NULL;
  int selfedge = 0;
  char buf[32];
  char *es = NULL;
  struct unode *d1 = NULL;
  struct unode *d3 = NULL;
  char *elabel = NULL;
  int color = 0;
  int textcolor = 0;
  int fontsize = 10;
  char *fontname = NULL;
  int textbold = 0;
  int textitalic = 0;
  int textoblique = 0;
  int style = 0;

  cur_node = cur;

  edgeid = xmlGetProp (cur_node, (const xmlChar *) "id");

  if (option_debug)
    {
      printf ("%s(): edgeid=%s\n", __FUNCTION__, (char *) edgeid);
    }

  edgesource = xmlGetProp (cur_node, (const xmlChar *) "source");

  fromname = uniqstring ((char *) edgesource);

  if (option_debug)
    {
      printf ("%s(): edgesource=%s\n", __FUNCTION__, (char *) edgesource);
    }

  edgetarget = xmlGetProp (cur_node, (const xmlChar *) "target");

  toname = uniqstring ((char *) edgetarget);


  if (option_debug)
    {
      printf ("%s(): edgetarget=%s\n", __FUNCTION__, (char *) edgetarget);
    }

  /* check for a loop to itself */
  if (strcmp (fromname, toname) == 0)
    {
      selfedge = 1;
    }
  else
    {
      selfedge = 0;
    }

  if (selfedge)
    {
      if (option_debug)
	{
	  printf ("%s(): edge %s is a selfedge\n", __FUNCTION__,
		  (char *) edgetarget);
	}
    }

  /* check for self edges */
  if (option_selfedges == 0)
    {
      if (selfedge)
	{
	  fn = uniqnode (fromname);
	  if (fn == (struct unode *) 0)
	    {
	      fn = unode_new (fromname);
	      fn->label = fn->name;
	      fn->rootedon = subg;
	      /* node is defined by edge statement */
	      fn->bitflags.defbyedge = 1;
	      uniqnode_add (fn->name, fn);
	      unode_append_nodelist (fn);
	      usubg_add_node (fn, subg);
	    }
	  return;
	}
    }
  /* */
  if (selfedge)
    {

      fn = uniqnode (fromname);
      if (fn == (struct unode *) 0)
	{
	  fn = unode_new (fromname);
	  fn->label = fn->name;
	  fn->rootedon = subg;
	  /* node is defined by edge statement */
	  fn->bitflags.defbyedge = 1;
	  uniqnode_add (fn->name, fn);
	  unode_append_nodelist (fn);
	  usubg_add_node (fn, subg);
	}
      memset (buf, 0, 32);
      snprintf (buf, 32 - 1, "___selfedge%d___", selfedgecount);
      es = uniqstring (buf);
      selfedgecount = selfedgecount + 1;
      d1 = unode_new (es);
      /* dummy node */
      d1->label = NULL;
      d1->rootedon = subg;
      /* node is defined by edge statement */
      d1->bitflags.defbyedge = 1;
      /* edgelabel node to draw different */
      d1->bitflags.edgelabel = 0;
      /* node is part of selfedge */
      d1->bitflags.selfedge = 1;
      uniqnode_add (d1->name, d1);
      unode_append_nodelist (d1);
      usubg_add_node (d1, subg);
      /* 1st edge */
      ue = uedge_new (fn, d1);
      ue->color = color;
      ue->style = style;
      ue->rootedon = subg;
      ue->bitflags.selfedge = 1;
      uedge_append_edgelist (ue);
      usubg_add_edge (ue, subg);
      /* next node */
      memset (buf, 0, 32);
      snprintf (buf, 32 - 1, "___selfedge%d___", selfedgecount);
      es = uniqstring (buf);
      selfedgecount = selfedgecount + 1;
      d3 = unode_new (es);
      /* dummy node */
      d3->label = NULL;
      d3->rootedon = subg;
      /* node is defined by edge statement */
      d3->bitflags.defbyedge = 1;
      /* edgelabel node to draw different */
      d3->bitflags.edgelabel = 0;
      /* node is part of selfedge */
      d3->bitflags.selfedge = 1;
      uniqnode_add (d3->name, d3);
      unode_append_nodelist (d3);
      usubg_add_node (d3, subg);
      /* 2nd edge */
      ue = uedge_new (d1, d3);
      ue->color = color;
      ue->style = style;
      ue->rootedon = subg;
      if (elabel)
	{
	  ue->label = elabel;
	  ue->textcolor = textcolor;
	  ue->fontsize = fontsize;
	  ue->fontname = fontname;
	  if (textbold)
	    {
	      ue->bitflags.textbold = 1;
	    }
	  if (textitalic)
	    {
	      ue->bitflags.textitalic = 1;
	    }
	  if (textoblique)
	    {
	      ue->bitflags.textoblique = 1;
	    }
	}
      ue->bitflags.selfedge = 1;
      uedge_append_edgelist (ue);
      usubg_add_edge (ue, subg);
      /* 4th edge */
      ue = uedge_new (d3, fn);
      ue->color = color;
      ue->style = style;
      ue->rootedon = subg;
      ue->bitflags.selfedge = 1;
      uedge_append_edgelist (ue);
      usubg_add_edge (ue, subg);
      return;
    }

  /* regular edge */
  fn = uniqnode (fromname);
  if (fn == (struct unode *) 0)
    {
      fn = unode_new (fromname);
      fn->label = fn->name;
      fn->rootedon = subg;
      /* node is defined by edge statement */
      fn->bitflags.defbyedge = 1;
      uniqnode_add (fn->name, fn);
      unode_append_nodelist (fn);
      usubg_add_node (fn, subg);
    }
  tn = uniqnode (toname);
  if (tn == (struct unode *) 0)
    {
      tn = unode_new (toname);
      tn->label = tn->name;
      tn->rootedon = subg;
      /* node is defined by edge statement */
      tn->bitflags.defbyedge = 1;
      uniqnode_add (tn->name, tn);
      unode_append_nodelist (tn);
      usubg_add_node (tn, subg);
    }
  ue = uedge_new (fn, tn);
  /* black edge line */
  ue->color = 0x00;
  /* solid edge line */
  ue->style = ESTYLE_NORMAL;
  ue->rootedon = subg;
  uedge_append_edgelist (ue);
  usubg_add_edge (ue, subg);

  for (cur_node = cur->children; cur_node != NULL; cur_node = cur_node->next)
    {
      if (option_debug)
	{
	  printf ("edge-ch %s <-\n", cur_node->name);
	}
    }

  return;
}

/* a hyper edge has multiple node connections */
static void
graphmlhyperedge (xmlNodePtr cur, struct usubg *subg)
{
  xmlNodePtr cur_node;

  if (subg)
    {
    }

  for (cur_node = cur->children; cur_node != NULL; cur_node = cur_node->next)
    {
      if (option_debug)
	{
	  printf ("hyperedge-ch %s <-\n", cur_node->name);
	}
    }

  return;
}

/* a locator statement */
static void
graphmllocator (xmlNodePtr cur, struct usubg *subg)
{
  xmlNodePtr cur_node;

  if (subg)
    {
    }

  for (cur_node = cur->children; cur_node != NULL; cur_node = cur_node->next)
    {
      if (option_debug)
	{
	  printf ("locator-ch %s <-\n", cur_node->name);
	}
    }

  return;
}

/* a data statement for current subgraph */
static void
graphmlgraphdata (xmlNodePtr cur, struct usubg *subg)
{
  xmlNodePtr cur_node;

  if (subg)
    {
    }

  for (cur_node = cur->children; cur_node != NULL; cur_node = cur_node->next)
    {
      if (option_debug)
	{
	  printf ("graphdata-ch %s <-\n", cur_node->name);
	}
    }

  return;
}

/* xml generated by GNU Bison parser generator */
static int
parsebisonxml (char *fname, FILE * fstream, xmlNodePtr cur)
{

  if (fname)
    {
    }
  if (fstream)
    {
    }
  if (cur)
    {
    }

  return (0);
}

/* End */
